package com.realshare.bby.service.integral;

import com.realshare.bby.model.RuleIntegralTask;
import com.realshare.frame.component.quartz.model.SysDbQuartz;
import com.realshare.frame.util.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.SQLReady;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;

/**
 * ${DESCRIPTION}
 * author leisure
 * email 15075107120@139.com
 * create 2019-02-21 上午 10:46
 * modify 2019-02-21 上午 10:46
 **/
@Service
public class IntegralCalculateControl {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private SQLManager sqlManager;
    @Autowired
    private IntegralReadyData readyData;
    @Autowired
    private IntegralCalculate calculate;
    @Autowired
    private TidyIntegralData tidyIntegralData;

    /**
     * 计算积分总控
     */
    void calculate() {
        log.info("开始执行积分计算");
        String beginTime = DateUtils.getNowDateTime();
        //判断前提预任务是否有异常，使用系统数据库任务
        SysDbQuartz sysQuartz = sqlManager.query(SysDbQuartz.class).andEq("status", "2").single();
        String str;
        if (sysQuartz != null) {
            str = "发现异常任务，导致数据库数据处理任务暂停，任务信息为：“" + sysQuartz.toString() + "”，计算积分任务被迫停止！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }

        //准备积分计算所需的数据
        str = readyData.readyData();
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，计算任务被迫停止！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }
        //计算正常用户组用户积分
        str = calculate.calculateBusinessIntegral(0, readyData.getRuleConfigs());
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，计算正常用户组用户积分任务被迫停止！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }
        String msg = "1、计算正常用户组用户积分计算完毕并成功； ";

        //补算用户组核验失败的用户被处理之后积分
        str = calculate.calculateBusinessIntegral(1, readyData.getRuleConfigs());
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，补算用户组核验失败的用户被处理之后积分任务被迫停止！，但【" + msg + "】！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }
        msg += "2、补算用户组核验失败的用户被处理之后积分计算完毕并成功； ";

        //整理数据，以及更新跑批服务程序未处理状态为已处理
        str = tidyIntegralData.tidyIntegralData();
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，处理清理一个账号多个规则重复计算给分/扣分的积分记录，保留给分/扣分最多的记录任务被迫停止！，但【" + msg + "】！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }
        msg += "3、清理一个账号多个规则重复计算给/扣分分的积分记录，保留给分/扣分最多的积分记录计算完毕并成功； ";

        //处理需要每期给分的积分
        str = calculateCycleGiveIntegral();
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，处理需要每期给分的积分任务被迫停止！，但【" + msg + "】！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }
        msg += "4、处理需要每期给分的积分计算完毕并成功； ";

        //处理需要扣分的积分
        str = calculateDeductIntegral();
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，处理需要扣分的积分任务被迫停止！，但【" + msg + "】！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }

        //积分计算收尾工作，处理待激活积分
        str = calculateFinishWork();
        if (StringUtils.isNotEmpty(str)) {
            str = str + "，执行积分计算收尾工作被迫停止！，但【" + msg + "】！";
            log.error(str);
            recordLog(str, 0, beginTime);
            return;
        }

        recordLog("积分计算跑批完毕并成功！", 1, beginTime);
        log.info("结束执行积分计算");
    }

    /**
     * 积分计算收尾工作
     *
     * @return 处理状态
     */
    private String calculateFinishWork() {
        log.info("开始执行积分计算收尾工作");
        try {
            //更新待激活积分，包含冻结积分（满足行唐手动冻结的积分）
            String beActiveSql = "update customer_means c INNER JOIN (" +
                    "select identity_code,sum(integral) integral from rule_integral_task " +
                    "where `status` = 2 GROUP BY identity_code" +
                    ") r on c.identity_code=r.IDENTITY_CODE " +
                    "set c.BE_ACTIVATE_INTEGRAL=r.integral+ifnull(c.FROZEN_INTEGRAL,0)";
            log.info("更新待激活积分");
            sqlManager.executeUpdate(new SQLReady(beActiveSql));
            return "";
        } catch (Exception e) {
            e.printStackTrace();
            return "执行积分计算收尾工作时出错，错误信息为：【" + e.getMessage() + "】";
        } finally {
            log.info("结束执行积分计算收尾工作");
        }
    }

    /**
     * 处理需要扣分的积分
     *
     * @return 处理状态
     */
    private String calculateDeductIntegral() {
        log.info("开始处理需要扣分的积分");
        try {
            String updateSql = "update CUSTOMER_MEANS set INTEGRAL=#{integral}# where IDENTITY_CODE = '%s'";

            //查询需要扣分的处理记录
            String sql = "select * from RULE_INTEGRAL_TASK where STATUS = 5";
            List<RuleIntegralTask> tasks = sqlManager.execute(new SQLReady(sql), RuleIntegralTask.class);
            for (RuleIntegralTask task : tasks) {
                String taskSql;
                String integral = "0";
                if (task.getResidueRatio() >= 1) {//超过百分百认为全部支取
                    //删除提前支取后无效的赠分-全部支取；
                    taskSql = "update RULE_INTEGRAL_TASK set STATUS=0 " +
                            "where ID_NO='" + task.getIdNo() + "' and DATA_TIME='" + task.getGiveDataTime() + "' ";
                    //积分类型：1.积分规则赠分；2.手工积分补贴；3.不扣分但停止给分；4.扣除指定分；5.扣除期前预赠送积分；6.扣除已赠送所有积分；
                    switch (task.getIntegralType()) {
                        case 3://不扣分但停止给分；
                            taskSql += " AND STATUS < 3 ";
                            integral = "0";
                            break;
                        case 4://扣除指定分；
                            taskSql += " AND STATUS < 3 ";
                            integral = "(select INTEGRAL from RULE_INTEGRAL_TASK where id = '" + task.getId() + "')";
                            break;
                        case 5://扣除期前预赠送积分；
                            taskSql += " AND STATUS < 3 ";
                            integral = "(select INTEGRAL from (" +
                                    "select sum(INTEGRAL) INTEGRAL,GIVE_TIME from RULE_INTEGRAL_TASK " +
                                    "where ID_NO = '" + task.getIdNo() + "'and STATUS = 3 " +
                                    "and RULE_CONFIG_ACTION_ID in (select id from RULE_CONFIG_GIVE where CONFIG_ID='1') " +
                                    "GROUP BY GIVE_TIME ORDER BY GIVE_TIME desc " +
                                    ") t limit 1)";
                            //更新最后一期预增分状态-全部支取；
                            String lastGiveSql = "update RULE_INTEGRAL_TASK set STATUS=0 where id in (" +
                                    "select t.id from (" +
                                    "select id,row_number() over(partition by RULE_CONFIG_ACTION_ID order by GIVE_TIME desc) rn from RULE_INTEGRAL_TASK " +
                                    "where ID_NO = '" + task.getIdNo() + "' and STATUS = 3 " +
                                    "and RULE_CONFIG_ACTION_ID in (select id from RULE_CONFIG_GIVE where CONFIG_ID = '1') " +
                                    ") t where rn = 1)";
                            sqlManager.executeUpdate(new SQLReady(lastGiveSql));
                            break;
                        case 6://扣除已赠送所有积分；
                            integral = "(select sum(INTEGRAL) from RULE_INTEGRAL_TASK " +
                                    "where ID_NO = '" + task.getIdNo() + "' and STATUS = 3 )";
                            break;
                    }
                } else {//部分支取
                    //删除提前支取后需要按比例扣除的赠分，一个规则仅扣除一次
                    taskSql = "update RULE_INTEGRAL_TASK set RESIDUE_RATIO='" + task.getResidueRatio() + "'" +
                            ",INTEGRAL=INTEGRAL*" + task.getResidueRatio() +
                            " where ID_NO='" + task.getIdNo() + "' and DATA_TIME='" + task.getGiveDataTime() + "' and RESIDUE_RATIO is null ";
                    //积分类型：1.积分规则赠分；2.手工积分补贴；3.不扣分但停止给分；4.扣除指定分；5.扣除期前预赠送积分；6.扣除已赠送所有积分；
                    switch (task.getIntegralType()) {
                        case 3://不扣分但停止给分；
                            taskSql += " AND STATUS < 3 ";
                            integral = "0";
                            break;
                        case 4://扣除指定分；
                            taskSql += " AND STATUS < 3 ";
                            integral = "(select INTEGRAL from RULE_INTEGRAL_TASK where id = '" + task.getId() + "')";
                            break;
                        case 5://需要按比例扣除期前预赠送积分；
                            taskSql += " AND STATUS < 3 ";
                            integral = "(select INTEGRAL from (" +
                                    "select sum(INTEGRAL) INTEGRAL,GIVE_TIME from RULE_INTEGRAL_TASK " +
                                    "where ID_NO = '" + task.getIdNo() + "'and STATUS = 3 " +
                                    "and RULE_CONFIG_ACTION_ID in (select id from RULE_CONFIG_GIVE where CONFIG_ID='1') " +
                                    "GROUP BY GIVE_TIME ORDER BY GIVE_TIME desc " +
                                    ") t limit 1)  and RESIDUE_RATIO is null";
                            //更新最后一期提前支取后需要按比例扣除的预赠分；
                            String lastGiveSql = "update RULE_INTEGRAL_TASK set RESIDUE_RATIO='" + task.getResidueRatio() + "'" +
                                    ",INTEGRAL=INTEGRAL*" + task.getResidueRatio() + " where id in (" +
                                    "select t.id from (" +
                                    "select id,row_number() over(partition by RULE_CONFIG_ACTION_ID order by GIVE_TIME desc) rn from RULE_INTEGRAL_TASK " +
                                    "where ID_NO = '" + task.getIdNo() + "' and STATUS = 3 " +
                                    "and RULE_CONFIG_ACTION_ID in (select id from RULE_CONFIG_GIVE where CONFIG_ID = '1') " +
                                    ") t where rn = 1)  and RESIDUE_RATIO is null";
                            sqlManager.executeUpdate(new SQLReady(lastGiveSql));
                            break;
                        case 6://需要按比例扣除已赠送所有积分；
                            integral = "(select sum(INTEGRAL*" + task.getResidueRatio() + ") from RULE_INTEGRAL_TASK " +
                                    "where ID_NO = '" + task.getIdNo() + "' and STATUS = 3 )";
                            break;
                    }
                }
                String prepareSql = String.format(updateSql, task.getIdentityCode());
                //扣除用户分数
                if (task.getAllowNegativeIntegra() == 2) {//不允许积分余额为负
                    //剩余积分够扣的情况，剩余积分=剩余积分-需扣积分
                    String pSql = prepareSql.replaceAll("#\\{integral}#", "ifnull(INTEGRAL,0)-" + integral) + " and INTEGRAL >= " + integral;
                    sqlManager.executeUpdate(new SQLReady(pSql));
                    //剩余积分不够扣的情况，剩余积分=0
                    pSql = prepareSql.replaceAll("#\\{integral}#", "0") + " and INTEGRAL <= " + integral;
                    sqlManager.executeUpdate(new SQLReady(pSql));
                } else {
                    String pSql = prepareSql.replaceAll("#\\{integral}#", "ifnull(INTEGRAL,0)-" + integral);
                    sqlManager.executeUpdate(new SQLReady(pSql));
                }
                //更新因扣分导致的失效赠分记录
                sqlManager.executeUpdate(new SQLReady(taskSql));
            }
            //添加流水记录
            String flowSql = "insert into INTEGRAL_FLOW(ID,IDENTITY_CODE,TYPE,TITLE,QUANTITY" +
                    ",DEAL_TYPE,IS_READ,DATA_TIME,CREATOR,CREATE_TIME,STATUS,CLEAR_STATUS,ORG_CODE) " +
                    "select ID,IDENTITY_CODE,'1',CONCAT('通过业务【',(select NAME from RULE_CONFIGS where id=t.RULE_CONFIG_ID),'】扣除积分') TITLE" +
                    ",-INTEGRAL,'1','0',GIVE_TIME,'system','%s','1','0',ORG_CODE from RULE_INTEGRAL_TASK t where STATUS = 5";
            log.info("添加流水记录");
            sqlManager.executeUpdate(new SQLReady(String.format(flowSql, DateUtils.getNowDateTime())));

            //更新待扣分状态为已扣分
            String deductTaskSql = "update RULE_INTEGRAL_TASK set STATUS = 6,REAL_CLAIM_TIME=GIVE_TIME where STATUS = 5";
            log.info("更新待扣分状态为已扣分");
            sqlManager.executeUpdate(new SQLReady(deductTaskSql));

            return "";
        } catch (Exception e) {
            e.printStackTrace();
            return "处理需要扣分的积分时出错，错误信息为：【" + e.getMessage() + "】";
        } finally {
            log.info("结束处理需要扣分的积分");
        }
    }

    /**
     * 处理需要每期给分的积分， 如果已经到了给积分时间，并且不需要领取，直接给积分
     */
    private String calculateCycleGiveIntegral() {
        log.info("开始处理需要每期给分的积分");
        try {
            String now = DateUtils.getNowDateTime();

            //更新积分
            String sql = "UPDATE CUSTOMER_MEANS t1 INNER JOIN (" +
                    "select t1.IDENTITY_CODE,t1.INTEGRAL+ifnull(t2.INTEGRAL,0) INTEGRAL from ( " +
                    "select IDENTITY_CODE,sum(INTEGRAL) INTEGRAL from RULE_INTEGRAL_TASK  " +
                    "where INTEGRAL_TYPE=1 and STATUS=2  and GIVE_TIME<='" + now + "' " +
                    "GROUP BY IDENTITY_CODE) t1  " +
                    "LEFT JOIN CUSTOMER_MEANS t2 on t1.IDENTITY_CODE=t2.IDENTITY_CODE " +
                    ") t2 ON T1.IDENTITY_CODE=T2.IDENTITY_CODE SET t1.INTEGRAL=t2.INTEGRAL";
            log.info("更新积分");
            sqlManager.executeUpdate(new SQLReady(sql));

            //添加流水记录
            String flowSql = "insert into INTEGRAL_FLOW(ID,IDENTITY_CODE,TYPE,TITLE,QUANTITY,DEAL_TYPE" +
                    ",IS_READ,DATA_TIME,CREATOR,CREATE_TIME,STATUS,CLEAR_STATUS,ORG_CODE) " +
                    "select ID,IDENTITY_CODE,'1',CONCAT('通过业务【',(select NAME from RULE_CONFIGS where id=t.RULE_CONFIG_ID),'】获取积分') TITLE" +
                    ",INTEGRAL,'1','0',GIVE_TIME,'system','%s','1','0',ORG_CODE from RULE_INTEGRAL_TASK t " +
                    "where INTEGRAL_TYPE=1 and STATUS=2 and GIVE_TIME<='" + now + "'";
            log.info("添加流水记录");
            sqlManager.executeUpdate(new SQLReady(String.format(flowSql, now)));

            //更新状态
            sql = "update RULE_INTEGRAL_TASK set STATUS=3,REAL_CLAIM_TIME=GIVE_TIME " +
                    "where INTEGRAL_TYPE=1 and STATUS=2 and GIVE_TIME<='" + now + "'";
            sqlManager.executeUpdate(new SQLReady(sql));

            return "";
        } catch (Exception e) {
            e.printStackTrace();
            return "处理需要每期给分的积分时出错，错误信息为：【" + e.getMessage() + "】";
        } finally {
            log.info("结束处理需要每期给分的积分");
        }
    }

    /**
     * 记录日志
     */
    private void recordLog(String describe, int status, String beginTime) {
        describe = describe.replaceAll("\'", "&#039;");
        String sql = "insert into RULE_CALCULATE_LOG(ID,`DESCRIBE`,BEGIN_TIME,END_TIME,STATUS) " +
                "values('" + UUID.randomUUID() + "','" + describe + "','" + beginTime + "','" +
                DateUtils.getNowDateTime() + "','" + status + "')";
        sqlManager.executeUpdate(new SQLReady(sql));
    }
}
